package util;

import java.io.*;
import java.util.ArrayList;
import java.util.Properties;
import java.util.Random;

import javax.xml.namespace.QName;
import javax.xml.parsers.*;
import javax.xml.xquery.*;

import net.sf.saxon.xqj.SaxonXQDataSource;

import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.eclipse.persistence.internal.oxm.schema.model.List;
import org.w3c.dom.*;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import domain.Movie;

import entity.Watched;

public class XmlUtil {

	private static String fileName= "catalog.xml";

	private static String selectMoviesByGenresAndWatchedness(ArrayList<String> genres, ArrayList<Watched> watched,String domainName) throws Exception {
		String filePath = domainName +"/"+ fileName;
		String result = "";
		if (genres.size() == 0)
			return result;
		SaxonXQDataSource ds = new SaxonXQDataSource();
		XQConnection conn = ds.getConnection();

		final String sep = System.getProperty("line.separator");
		String queryString = "declare variable $docName as xs:string external;"
				+ sep + " for $dvd in doc($docName)/catalog/DVD " + " where (";
		
		for (String genre : genres) {
			queryString += "$dvd/genre/text()='" + genre + "'";
			if (genres.lastIndexOf(genre) != genres.size() - 1)
				queryString += " or ";
			else queryString += " ) ";
			if ( genres.lastIndexOf(genre) == genres.size() - 1 && watched.size() > 0)
				queryString += " and (";
		}
		for (Watched watch : watched) {
			queryString += "$dvd/id/text()!='" + watch.getMovieId() + "'";
			if (watched.lastIndexOf(watch) != watched.size() - 1)
				queryString += " and ";
			else queryString += " ) ";
		}
		queryString += " return " + "<DVD>" + "<id>{$dvd/id/text()}</id>"
				+ "<name>{$dvd/name/text()}</name>"
				+ "<director>{$dvd/director/text()}</director>"
				+ "<year>{$dvd/year/text()}</year>"
				+ " <genre>{$dvd/genre/text()}</genre>" + "</DVD>";

		XQExpression expr = conn.createExpression();
		expr.bindString(new QName("docName"), filePath,
				conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
		XQSequence results = expr.executeQuery(queryString);
		result = results.getSequenceAsString(new Properties());

		return result;
	}

	private static String selectMoviesByIds(ArrayList<Watched> watched,String domainName) throws Exception	{
		String filePath = domainName +"/"+ fileName;
		String result = "";
		if(watched.size()==0)
			return "";
		SaxonXQDataSource ds = new SaxonXQDataSource();
		XQConnection conn = ds.getConnection();

		final String sep = System.getProperty("line.separator");
		String queryString = "declare variable $docName as xs:string external;"
				+ sep + " for $dvd in doc($docName)/catalog/DVD " + " where ";
		
		for (Watched watch : watched) {
			queryString += "$dvd/id/text()='" + watch.getMovieId() + "'";
			if (watched.lastIndexOf(watch) != watched.size() - 1)
				queryString += " or ";
		}
		queryString += " return " + "<DVD>" + "<id>{$dvd/id/text()}</id>"
				+ "<name>{$dvd/name/text()}</name>"
				+ "<director>{$dvd/director/text()}</director>"
				+ "<year>{$dvd/year/text()}</year>"
				+ " <genre>{$dvd/genre/text()}</genre>" + "</DVD>";

		XQExpression expr = conn.createExpression();
		expr.bindString(new QName("docName"), filePath,	conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
		XQSequence results = expr.executeQuery(queryString);
		result = results.getSequenceAsString(new Properties());

		return result;
	}
	
	private static Document loadXMLFromString(String xml) throws Exception {
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = factory.newDocumentBuilder();
		InputSource is = new InputSource(new StringReader(xml));
		return builder.parse(is);
	}
	

	public static Movie getRandomRecommendation(ArrayList<String> genres, ArrayList<Watched> watched, String domainName) throws Exception  {
		Movie movie=null;
		String xml = "<?xml version=\"1.0\"?><catalog>"
				+ selectMoviesByGenresAndWatchedness(genres, watched,domainName)
				+ "</catalog>";
		Document doc = loadXMLFromString(xml);
		// normalize text representation
		doc.getDocumentElement().normalize();
		NodeList dvds = doc.getElementsByTagName("DVD");
		int count = dvds.getLength();
		if(count==0)
			return null;
		int which = (new Random()).nextInt(count);
		Node recommend = dvds.item(which);
		if(recommend!=null)
		{
			movie=new Movie();
			Node current = recommend.getFirstChild();
			while(null != current) {
				String content=current.getTextContent();
				String name=current.getNodeName();
				if(!content.equals("")){
					if(name.equals("id"))
						movie.setId(content);
					if(name.equals("name"))
						movie.setName(content);
					if(name.equals("director"))
						movie.setDirector(content);
					if(name.equals("year"))
						movie.setYear(content);
					if(name.equals("genre"))
						movie.setGenre(content);
				}
			    current = current.getNextSibling();
			}
		}
		return movie;
	}
	
	public static ArrayList<Movie> getWatchedMovies(ArrayList<Watched> watched, String domainName) throws Exception {
		ArrayList<Movie> movies=new ArrayList<Movie>();
		String xml = "<?xml version=\"1.0\"?><catalog>"
				+ selectMoviesByIds(watched,domainName)
				+ "</catalog>";
		Document doc = loadXMLFromString(xml);
		// normalize text representation
		doc.getDocumentElement().normalize();
		NodeList dvds = doc.getElementsByTagName("DVD");
		int count = dvds.getLength();
		if(count==0)
			return movies;
		for(int i=0;i<count;i++){
			Node recommend = dvds.item(i);
			if(recommend!=null)
			{
				Movie movie=new Movie();
				Node current = recommend.getFirstChild();
				while(null != current) {
					String content=current.getTextContent();
					String name=current.getNodeName();
					if(!content.equals("")){
						if(name.equals("id"))
							movie.setId(content);
						if(name.equals("name"))
							movie.setName(content);
						if(name.equals("director"))
							movie.setDirector(content);
						if(name.equals("year"))
							movie.setYear(content);
						if(name.equals("genre"))
							movie.setGenre(content);
					}
				    current = current.getNextSibling();
				}
				movies.add(movie);
			}
		}
		return movies;
	}
}
